// Copyright (C) 2010  Joan Puigcerver Perez, <joapuipe@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <People.hpp>

#include <iostream>
#include <fstream>
#include <cstring>
#include <typeinfo>

#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLString.hpp>

#include <SAXPeopleHandler.hpp>

People::~People()
{
  for(std::vector<Person*>::iterator it = people.begin(); it != people.end(); ++it)
    delete *it;

  for(vectIterator it = people_names.begin(); it != people_names.end(); ++it)
    delete [] (const char*)it->first;
}

bool People::load(Database * database)
{

  try {
    XMLPlatformUtils::Initialize();
  } 
  catch( const XMLException & toCatch ) {
    char* message = XMLString::transcode(toCatch.getMessage());
    std::cerr << "Error during initialization! :" << std::endl;
    std::cerr << "Exception message is:" << std::endl
	      << message << std::endl;
    XMLString::release(&message);
    return false;
  }
  
  SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
  
  SAXPeopleHandler handler(database);
  parser->setContentHandler(&handler);
  parser->setErrorHandler(&handler);
  
  try {
    parser->parse(PEOPLE_XML);
  }
  catch (const XMLException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    std::cerr << "Exception message is:" << std::endl
	      << message << std::endl;
    XMLString::release(&message);
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  catch (const SAXParseException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    std::cerr << "Exception message is:" << std::endl
	      << message << std::endl;
    XMLString::release(&message);
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  catch (...) {
    std::cerr << "Unexpected Exception" << std::endl ;
    delete parser;
    XMLPlatformUtils::Terminate();
    return false;
  }
  
  delete parser;
  XMLPlatformUtils::Terminate();
  
  people_names.reserve( people_map.size() );
  for(mapIterator it = people_map.begin(); it != people_map.end(); ++it)
    people_names.push_back( *it );

  people_map.clear();
  
  return true;

}

Person * People::add_person(const std::string& name)
{
  char * _name = new char [name.length() + 1];
  strcpy(_name, name.c_str());

  std::pair<mapIterator,bool> ret = people_map.insert ( mapPair(_name, MapNode()) );
  if (ret.second == false ) delete [] _name; // It deletes the repeated string.

  if ( ret.second == true || ret.first->second.main == 0 ) { 
    Person * res = new Person(people.size(), ret.first->first);
    assert( res != 0 );
    people.push_back( res );
    ret.first->second.main = res;
    return res;
  } else {
    return (Person*)ret.first->second.main;
  }
}

void People::add_aka(const std::string& aka, Person* person)
{
  char * _aka = new char [aka.length() + 1];
  strcpy(_aka, aka.c_str());

  std::pair<mapIterator,bool> ret = people_map.insert ( mapPair(_aka, MapNode()) );
  if( ret.second == false ) delete [] _aka; // It deletes the repeated string.

  ret.first->second.akas.push_back( person );
}

std::pair< bool, std::list<const Person*> > People::search(const std::string& name) const
{
  std::list<const Person*> lst;
  size_t l = 0, r = people_names.size()-1;

  while( l <= r ) {
    size_t m = (l+r)/2;
    if ( people_names[m].first == name ){  
      if( people_names[m].second.main ) 
	lst.push_back( (Person*)(people_names[m].second.main) ); 
      else
	for(std::list<Node*>::const_iterator it = people_names[m].second.akas.begin(); 
	    it != people_names[m].second.akas.end(); ++it)
	  lst.push_back( (Person*)(*it) );
      
      return std::pair<bool, std::list<const Person*> >(true, lst);
    } else if ( people_names[m].first > name ){ 
      r=m-1; 
      if (people_names[r].first < name) break;
    } else { 
      l=m+1; 
      if (people_names[l].first > name) break;
    }
  }

  for(size_t i = l; i <= r; ++i) {
    if( people_names[i].second.main ) 
      lst.push_back( (Person*)(people_names[i].second.main) ); 
    for(std::list<Node*>::const_iterator it = people_names[i].second.akas.begin(); 
	it != people_names[i].second.akas.end(); ++it)
      lst.push_back( (Person*)(*it) );
  }
  
  return std::pair<bool, std::list<const Person*> >(false, lst);
}

size_t People::size(void) const
{
  return people.size();
}
